var fs = require('fs'),
  util = require('util'),
  union = require('union'),
  ecstatic = require('ecstatic'),
  path = require('path'),
  url = require("url"),
  nodeCgiProcessor = require("../modules/node-cgi"),
  _ = require('underscore');

function matchContext(requestPath, clazz, mapping) {
  if ((typeof mapping === "string" && requestPath.indexOf(mapping) !== -1) || (_.isRegExp(mapping) && mapping.test(requestPath))) {
    /**
     * cite: http://www.tuicool.com/articles/RVrm6z
     */
    delete require.cache[path.resolve('.', clazz)];
    try {
      var servlet = require(path.resolve('.', clazz));
      if (!servlet)
        return null;
      else if (servlet[requestPath])
        return servlet[requestPath];
      else {
        for (var param in servlet) {
          if (requestPath.indexOf(param) !== -1) {
            return servlet[param];
          }
        }
      }
    } catch (e) {
      throw e;
    }
  }
}

function loadContext(requestPath, clazz, mapping) {
  if (_.isArray(mapping)) {
    for (var i = 0; i < mapping.length; i++) {
      var matchedContext = matchContext(requestPath, clazz, mapping[i]);
      if (matchedContext)
        return matchedContext;
    }
  } else {
    return matchContext(requestPath, clazz, mapping);
  }
}

var HTTPServer = exports.HTTPServer = function(options, log) {
  options = options || {};

  if (options.root) {
    options.root = options.root;
  } else {
    try {
      fs.lstatSync('./public');
      options.root = './public';
    } catch (err) {
      options.root = './';
    }
  }

  if (options.headers) {
    options.headers = options.headers;
  }

  options.cache = options.cache || 3600; // in seconds.
  options.showDir = options.showDir !== 'false';
  options.autoIndex = options.autoIndex !== 'false';

  if (options.ext) {
    options.ext = options.ext === true ? 'html' : options.ext;
  }

  var before = [];

  // log filter
  before.push(function(req, res) {
    options.logFn && options.logFn(req, res);
    res.emit('next');
  });

  var config = require('../conf/ns.conf.js');

  if (config.filter) {
    before.push(function(req, res) {
      res.setHeader("Content-Type", 'text/html');
      if (_.isArray(config.filter)) {
        var flag = false;
        for (var i = 0; i < config.filter.length; i++) {
          var loadedContext = loadContext(url.parse(req.url).pathname, config.filter[i].clazz, config.filter[i].mapping);
          if (loadedContext) {
            loadedContext(req, res, config.filter[i], options);
            flag = true;
            break;
          }
        }
        if (flag === false)
          res.emit('next');
      } else {
        var loadedContext = loadContext(url.parse(req.url).pathname, config.filter.clazz, config.filter.mapping);
        if (loadedContext) {
          loadedContext(req, res, config.filter, options);
        } else {
          res.emit('next');
        }
      }
    });
  }

  before.push(function(req, res) {
    var pathname = url.parse(req.url).pathname; // "/" "/foo"
    if (options.cgiPath && nodeCgiProcessor && pathname.indexOf("/cgi-bin/") === 0) {
      var filename = path.resolve(path.resolve(options.cgiPath) + pathname.replace("/cgi-bin", ""));
      if (path.extname(pathname) === ".js") {
        try {
          nodeCgiProcessor.processFile(filename, {
            req: req,
            res: res
          });
        } catch (e) {
          res.end(e.message);
        }
      } else if (path.extname(pathname) === ".nd") {
        try {
          var httpResponse = nodeCgiProcessor.processFile(filename, {
            req: req,
            res: null
          });
          var headers = httpResponse.headers();
          for (var key in headers) {
            res.setHeader(key, headers[key]);
          }
          res.end(httpResponse.content());
        } catch (e) {
          res.end(e.message);
        }
      } else
        res.emit('next');
    } else
      res.emit('next');
  });

  before.push(ecstatic({
    root: options.root,
    cache: options.cache,
    showDir: options.showDir,
    autoIndex: options.autoIndex,
    defaultExt: options.ext
  }));

  this.server = union.createServer({
    before: (options.before || []).concat(before),
    headers: options.headers || {}
  });
};

HTTPServer.prototype.listen = function() {
  this.server.listen.apply(this.server, arguments);
};

HTTPServer.prototype.close = function() {
  return this.server.close();
};

exports.createServer = function(options, log) {
  return new HTTPServer(options, log);
};
